package es.upv.tricia.activity;

import java.io.IOException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import es.upv.tricia.Game;
import es.upv.tricia.Game.GameListener;
import es.upv.tricia.LocalGame;
import es.upv.tricia.Question;
import es.upv.tricia.R;
import es.upv.tricia.RestGame;
import es.upv.tricia.Tricia;
import es.upv.tricia.util.ToolKit;

public class PlayActivity extends Activity implements GameListener,
		OnClickListener
{
	private final class QuestionLoader
	{
		private static final int QUESTION_LOADED = 1;

		private static final int DELAY_COMPLETE = 2;

		private long mStopTimeInFuture;

		private boolean mQuestionLoaded;

		public synchronized void start(int questionNo, long delay)
		{
			Log.d(TAG, "Loading question");
			mStopTimeInFuture = SystemClock.elapsedRealtime() + delay;
			mQuestionLoaded = false;
			mGame.loadQuestion(questionNo);
			mHandler.removeMessages(DELAY_COMPLETE);
			mHandler.sendMessageDelayed(mHandler.obtainMessage(DELAY_COMPLETE),
					delay);
		}

		public synchronized void stop(Question question)
		{
			long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
			millisLeft = millisLeft > 0 ? millisLeft : 0;

			mQuestionLoaded = true;
			mHandler.removeMessages(QUESTION_LOADED);
			mHandler.sendMessageDelayed(
					mHandler.obtainMessage(QUESTION_LOADED, question),
					millisLeft);
		}

		private Handler mHandler = new Handler() {
			@Override
			public void handleMessage(Message msg)
			{
				synchronized (QuestionLoader.this) {
					Log.d(TAG, "Message received");
					if (msg.what == DELAY_COMPLETE && !mQuestionLoaded) {
						displayQuestionLoading();
					} else if (msg.what == QUESTION_LOADED) {
						displayQuestion((Question) msg.obj, 0);
					}
				}
			}
		};
	}

	private final class ProgressTimer extends CountDownTimer
	{
		private final ProgressBar mProgressBar;

		private float mProgress;

		private ProgressTimer(float progress)
		{
			super((long) ((1 - progress) * QUESTION_TIME), QUESTION_STEP);

			mProgressBar = (ProgressBar) findViewById(R.id.timeProgressBar);

			setProgress(progress);
		}

		@Override
		public void onTick(long millisUntilFinished)
		{
			// progress in [0,1]
			setProgress((QUESTION_TIME - millisUntilFinished)
					/ (float) QUESTION_TIME);
		}

		@Override
		public void onFinish()
		{
			setProgress(1);

			if (mIsRunning) {
				mLayout.setBackgroundColor(0xff990000);
				loadNextQuestion(0);
			}
		}

		public float getProgress()
		{
			return mProgress;
		}

		public void setProgress(float progress)
		{
			mProgress = progress;

			if (mIsRunning) {
				mProgressBar.setProgress((int) (progress * 100));
			}
		}
	}

	final private static long QUESTION_TIME = 5000;

	final private static long QUESTION_STEP = 50;

	private static final long QUESTION_DELAY = 1000;

	private static final String PREFS_NAME = "PlayState";

	private static final String TAG = "PlayActivity";

	private SharedPreferences mPrefs;

	private ProgressTimer mProgressTimer;

	private QuestionLoader mQuestionLoader;

	private Game mGame;

	private Question mQuestion;

	private float mProgress;

	/**
	 * Indicates whether the activity is running or paused
	 */
	private boolean mIsRunning = false;

	private int mNumberOfQuestions;

	private int mNextQuestion;

	private int mScore;

	private TextView mQuestionText;

	private TextView mScoreText;

	private Button mAnswer1Button;

	private Button mAnswer2Button;

	private Button mAnswer3Button;

	private Button mAnswer4Button;

	private ProgressDialog mLoadingDialog;

	private LinearLayout mLayout;

	private String mEmail;

	private String mUsername;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.play);

		mPrefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
		mLayout = (LinearLayout) findViewById(R.id.playLayout);
		mQuestionText = (TextView) findViewById(R.id.questionText);
		mScoreText = (TextView) findViewById(R.id.scoreText);
		mAnswer1Button = (Button) findViewById(R.id.answer1Button);
		mAnswer2Button = (Button) findViewById(R.id.answer2Button);
		mAnswer3Button = (Button) findViewById(R.id.answer3Button);
		mAnswer4Button = (Button) findViewById(R.id.answer4Button);
		mLoadingDialog = new ProgressDialog(this);
		mLoadingDialog.setMessage(getText(R.string.loadingNextQuestion));

		mAnswer1Button.setOnClickListener(this);
		mAnswer2Button.setOnClickListener(this);
		mAnswer3Button.setOnClickListener(this);
		mAnswer4Button.setOnClickListener(this);

		SharedPreferences settings = getSharedPreferences(
				Tricia.SETTINGS_PREFS_NAME, MODE_PRIVATE);

		mEmail = settings.getString("email", null);
		mUsername = settings.getString("name", null);

		mQuestionLoader = new QuestionLoader();

		if (ToolKit.isInternetAvailable(this)) {
			Log.d(TAG, "internet available");
			mGame = new RestGame();
		} else {
			Log.d(TAG, "internet not available");
			mGame = new LocalGame(this);
		}

		mGame.initialize(mEmail);
		mGame.addGameListener(this);

		if (savedInstanceState == null) {
			restoreSharedPreferences();
		}
	}

	@Override
	protected void onResume()
	{
		super.onResume();

		if (mEmail == null || mEmail.length() == 0 || mUsername == null
				|| mUsername.length() == 0) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage(
					String.format(getText(R.string.settingsRequiredMessage)
							.toString(), mScore)).setNeutralButton(R.string.ok,
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which)
						{
							dialog.dismiss();
							PlayActivity.this.finish();
						}
					});

			builder.create().show();

			return;
		}

		mIsRunning = true;

		if (mQuestion != null) {
			Log.d(TAG, "Restoring current question");

			displayQuestion(mQuestion, mProgress);
		} else {
			mLoadingDialog.show();
			mGame.start();
			Log.d(TAG, "Loaded new game");
		}
	}

	@Override
	protected void onPause()
	{
		super.onPause();

		mIsRunning = false;

		saveSharedPreferences();
	}

	@Override
	protected void onStop()
	{

		super.onStop();

		if (isFinishing()) {
			Log.d(TAG, "Stopping game");
			if (mGame != null) {
				mGame.stop();
				mGame = null;
				Log.d(TAG, "Stopped game");
			}

			if (mLoadingDialog != null) {
				mLoadingDialog.dismiss();
			}
			clearSharedPreferences();
		}
	}

	@Override
	protected void onRestoreInstanceState(Bundle state)
	{
		super.onRestoreInstanceState(state);

		mProgress = state.getFloat("progress");
		mNextQuestion = state.getInt("nextQuestion");
		mNumberOfQuestions = state.getInt("numberOfQuestions");
		mScore = state.getInt("score");

		if (state.getString("subject") != null) {
			mQuestion = new Question();
			mQuestion.setSubject(state.getString("subject"));
			mQuestion.setQuestionText(state.getString("questionText"));
			mQuestion.setAnswers(new String[] { state.getString("answer1"),
					state.getString("answer2"), state.getString("answer3"),
					state.getString("answer4") });
			mQuestion.setRightAnswer(state.getInt("rightAnswer"));
			mQuestion.setHelp(state.getInt("help"));
		}

		Log.d(TAG,
				"Restored from instance state: " + mQuestion.getQuestionText());
	}

	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);

		outState.putInt("nextQuestion", mNextQuestion);
		outState.putInt("numberOfQuestions", mNumberOfQuestions);
		outState.putInt("score", mScore);

		if (mProgressTimer != null) {
			outState.putFloat("progress", mProgressTimer.getProgress());
			mProgressTimer.cancel();
		}

		if (mQuestion != null) {
			outState.putString("subject", mQuestion.getSubject());
			outState.putString("questionText", mQuestion.getQuestionText());
			outState.putString("answer1", mQuestion.getAnswers()[0]);
			outState.putString("answer2", mQuestion.getAnswers()[1]);
			outState.putString("answer3", mQuestion.getAnswers()[2]);
			outState.putString("answer4", mQuestion.getAnswers()[3]);
			outState.putInt("rightAnswer", mQuestion.getRightAnswer());
			outState.putInt("help", mQuestion.getHelp());
		}
	}

	private void restoreSharedPreferences()
	{
		mProgress = mPrefs.getFloat("progress", 0);
		mNextQuestion = mPrefs.getInt("nextQuestion", 0);
		mNumberOfQuestions = mPrefs.getInt("numberOfQuestions", 0);
		mScore = mPrefs.getInt("score", 0);

		if (mPrefs.getString("subject", null) != null) {
			mQuestion = new Question();
			mQuestion.setSubject(mPrefs.getString("subject", null));
			mQuestion.setQuestionText(mPrefs.getString("questionText", null));
			mQuestion.setAnswers(new String[] {
					mPrefs.getString("answer1", null),
					mPrefs.getString("answer2", null),
					mPrefs.getString("answer3", null),
					mPrefs.getString("answer4", null) });
			mQuestion.setRightAnswer(mPrefs.getInt("rightAnswer", 0));
			mQuestion.setHelp(mPrefs.getInt("help", 0));
		}
	}

	private void saveSharedPreferences()
	{
		SharedPreferences.Editor edit = mPrefs.edit();

		edit.putInt("nextQuestion", mNextQuestion);
		edit.putInt("numberOfQuestions", mNumberOfQuestions);
		edit.putInt("score", mScore);

		if (mProgressTimer != null) {
			edit.putFloat("progress", mProgressTimer.getProgress());
			mProgressTimer.cancel();
		}

		if (mQuestion != null) {
			edit.putString("subject", mQuestion.getSubject());
			edit.putString("questionText", mQuestion.getQuestionText());
			edit.putString("answer1", mQuestion.getAnswers()[0]);
			edit.putString("answer2", mQuestion.getAnswers()[1]);
			edit.putString("answer3", mQuestion.getAnswers()[2]);
			edit.putString("answer4", mQuestion.getAnswers()[3]);
			edit.putInt("rightAnswer", mQuestion.getRightAnswer());
			edit.putInt("help", mQuestion.getHelp());
		}

		edit.commit();
	}

	private void clearSharedPreferences()
	{
		SharedPreferences.Editor edit = mPrefs.edit();
		edit.clear();
		edit.commit();
	}

	private void loadNextQuestion(long delay)
	{
		Log.i(TAG, "Loading next question: " + mNextQuestion + " from "
				+ mNumberOfQuestions);

		if (mNextQuestion >= mNumberOfQuestions) {
			mGame.publishScore(mUsername, mScore);

			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage(
					String.format(getText(R.string.gameOverMessage).toString(),
							mScore)).setNeutralButton(R.string.ok,
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which)
						{
							dialog.dismiss();
							PlayActivity.this.finish();
						}
					});

			builder.create().show();
		} else {
			mQuestionLoader.start(mNextQuestion++, delay);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		getMenuInflater().inflate(R.menu.play, menu);

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		return super.onOptionsItemSelected(item);
	}

	@Override
	public void onGameStart(int numberOfQuestions)
	{
		Log.d(TAG, "onGameStart: " + mIsRunning);
		if (mIsRunning) {
			Log.d(TAG, "onGameStart");
			mNumberOfQuestions = numberOfQuestions;

			loadNextQuestion(0);
		}
	}

	@Override
	public void onQuestionLoaded(Question question)
	{
		if (mIsRunning) {
			Log.i(TAG, "Loaded question \"" + question.getQuestionText() + "\"");

			mQuestionLoader.stop(question);
		}
	}

	private void displayQuestionLoading()
	{
		mLoadingDialog.show();
	}

	private void displayQuestion(Question question, float progress)
	{
		mLayout.setBackgroundColor(Color.BLACK);
		mLoadingDialog.hide();

		mQuestionText.setText(question.getQuestionText());
		mAnswer1Button.setText(question.getAnswers()[0]);
		mAnswer2Button.setText(question.getAnswers()[1]);
		mAnswer3Button.setText(question.getAnswers()[2]);
		mAnswer4Button.setText(question.getAnswers()[3]);

		mQuestion = question;
		mProgressTimer = new ProgressTimer(mProgress);
		mProgressTimer.start();
	}

	@Override
	public void onGameStop()
	{
		if (mIsRunning) {
		}
	}

	@Override
	public void onPublishScore()
	{
		if (mIsRunning) {
		}
	}

	@Override
	public void onClick(View v)
	{
		int givenAnswer;

		switch (v.getId()) {
		case R.id.answer1Button:
			givenAnswer = 1;
			break;
		case R.id.answer2Button:
			givenAnswer = 2;
			break;
		case R.id.answer3Button:
			givenAnswer = 3;
			break;
		case R.id.answer4Button:
			givenAnswer = 4;
			break;
		default:
			return;
		}

		if (mProgressTimer == null || mProgressTimer.getProgress() >= 1) {
			return;
		}

		float progress = mProgressTimer.getProgress();

		// for better UI responsiveness, set the progress bar to full
		mProgressTimer.setProgress(1);
		mProgressTimer.cancel();

		if (mQuestion != null) {
			if (mQuestion.getRightAnswer() == givenAnswer) {
				mScore += (1 - progress) * 100;
				mScoreText.setText(Integer.toString(mScore));
				mLayout.setBackgroundColor(0xff009900);
			} else {
				mLayout.setBackgroundColor(0xff990000);
			}

			loadNextQuestion(QUESTION_DELAY);
		}
	}

	@Override
	public void onGameFailed(Throwable reason)
	{
		if (mIsRunning) {
			mIsRunning = false;

			if (reason instanceof IOException) {
				AlertDialog.Builder builder = new AlertDialog.Builder(this);
				builder.setMessage(
						String.format(getText(R.string.connTimeoutMessage)
								.toString(), mScore)).setNeutralButton(
						R.string.ok, new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog,
									int which)
							{
								dialog.dismiss();
								PlayActivity.this.finish();
							}
						});
				builder.create().show();
			}
		}
	}
}
